<?php
/**
 * 小程序service
 * Created by PhpStorm.
 * User: lirui
 * Date: 2019/11/16
 * Time: 下午7:27
 */

namespace App\Services;

use App\Models\Order;
use App\Models\PayRequestData;
use App\Models\PayResponseData;
use App\Models\SystemConfig;
use App\Models\User;
use App\Modules\Liveapi\Services\MiniProgramService;
use EasyWeChat\Factory;
use EasyWeChat\Kernel\Support\XML;
use Illuminate\Http\Response;
use Illuminate\Support\Facades\Log;

/**
 * 小程序服务
 * Class MiniProgramService
 * @package App\Services
 */
class WechatService
{
    private static $instances = null;

    /**
     * 获取实例
     * @param $appType
     * @param string $type
     * @return mixed
     */
    public static function getApp($appType, $type='payment')
    {
        if (isset(self::$instances[$appType])) {
            return self::$instances[$appType];
        }

        $config = self::getConfig($appType,$type);
        $app = Factory::$appType($config);
        self::$instances[$appType] = $app;


        return $app;
    }

    /**
     * 获取参数配置
     * @param $appType
     * @param $type
     * @return array
     */
    private static function getConfig($appType, $type='payment')
    {
        //21是微信配置
        $wxapp = SystemConfig::whereType(21)->select('config')->first();
        if($wxapp){
            $wxapp = $wxapp->config;
        }
        $config = [
            'response_type' => 'array',
        ];


        switch ($appType) {
            case 'miniProgram':
                $config['app_id'] = $wxapp['key'];
                $config['secret'] = $wxapp['secret'];
                break;
            case 'payment':
            case 'wxapp':
                $payment = ConfigService::getSysValueByType(9);
                $notifyUrl = route('wechat.notify');
                $config['mch_id'] = trim($payment['mch_id']);
                $config['key'] = trim($payment['apikey']);
                $config['cert_path'] = storage_path('app/' . $payment['cert_file']);
                $config['key_path'] = storage_path('app/' . $payment['key_file']);
                $config['notify_url'] = $notifyUrl;
                if($type == 'payment'){
                    $config['app_id'] = $wxapp['key'];
                }elseif($type == 'wxapp'){
                    $config['app_id'] = "wxe39685ff021a6e83";
                }
                break;
        }

        return $config;
    }

    /**
     * 小程序实例
     * @return \EasyWeChat\MiniProgram\
     */
    public static function miniprogram()
    {
        return self::getApp('miniProgram');
    }

    /**
     * 支付实例
     * @param $type
     * @return mixed
     */
    public static function payment($type='payment')
    {
        return self::getApp('payment',$type);
    }

    /**
     *
     * @return mixed
     */
    public static function wxapp_pay()
    {
        return self::getApp('app');
    }

    /**
     * 微信文本安全内容检测
     * @param $content
     * @return mixed $result
     */
    public static function contentSecurity($content)
    {
        $app = self::getApp('miniProgram');
        return $app->content_security->checkText($content);
    }

    /**
     * 小程序支付回调
     */
    public static function handleNotify()
    {
        $request = request();
        $response = $request->getContent();
        $response = XML::parse($response);

        $orderId = $response['out_trade_no'];

        $order = Order::getByOrderId($orderId);


        if (!$order) {
            Log::error("回调订单数据异常:", $response);
        }

        //回调日志记录
        PayResponseData::record($orderId, $response['attach'], $response);

        $app = WechatService::payment();

        $response = $app->handlePaidNotify(function ($message, $fail) use ($order, $response) {
            // 使用通知里的 "微信支付订单号" 或者 "商户订单号" 去自己的数据库找到订单
            if (!$order || $order->paid) { // 如果订单不存在 或者 订单已经支付过了
                return true; // 告诉微信，我已经处理完了，订单没找到，别再通知我了
            }

            ///////////// <- 建议在这里调用微信的【订单查询】接口查一下该笔订单的情况，确认是已经支付 /////////////

            if ($message['return_code'] === 'SUCCESS') { // return_code 表示通信状态，不代表支付状态
                // 用户是否支付成功
                if (array_get($message, 'result_code') === 'SUCCESS') {
                    $transactionId = (string)$message['transaction_id'];

                    OrderService::jsPayPrice($order->order_id, $order->uid, $order->pay_type, $response['attach']);

                    // 用户支付失败
                } elseif (array_get($message, 'result_code') === 'FAIL') {
                    $order->notify_fail = array_get($message, 'return_msg');
                    $order->save(); // 保存订单
                }
            } else {
                return $fail('通信失败，请稍后再通知我');
            }

            return true; // 返回处理完成
        });

        $response->send(); // return $response;
    }

    /**
     * JSAPI--JSAPI支付（或小程序支付）
     * @param $orderId
     * @param $goodsName
     * @param string $attach 附件字符串
     * @return mixed
     * @throws
     */
    public static function appPay($orderId, $goodsName = '', $attach = 'legwork'){
        $data = self::createWxOrder($orderId, $goodsName, $attach, 'APP');

        $data['package'] = 'Sign=WXPay';
        return $data;

    }

    /**
     * 小程序支付参数
     * @param $orderId
     * @param string $attach 附件字符串
     * @return mixed
     * @throws
     */
    public static function jsPay($orderId, $goodsName = '', $attach = 'product', $isGroup = false)
    {
        $app = self::payment();
        $jssdk = $app->jssdk;
        $prepay = self::createWxOrder($orderId, $goodsName, $attach, 'JSAPI');

        return $jssdk->sdkConfig($prepay['prepay_id']); // 返回数组
    }

    /**
     * 微信统一下单
     * @param $orderId
     * @param string $goodsName
     * @param string $attach
     * @param string $trade_type
     * @return mixed
     * @throws \Exception
     */
    public static function createWxOrder($orderId, $goodsName = '', $attach = 'legwork', $trade_type='JSAPI')
    {
        if (is_string($orderId)) {
            $orderInfo = Order::getByOrderId($orderId);

        } else {
            $orderInfo = $orderId;
        }
        if (!$orderInfo || !isset($orderInfo['paid'])) {
            exception('支付订单不存在!');
        }
        if ($orderInfo['paid']) {
            exception('订单已支付!');
        }
        if ($orderInfo['pay_price'] <= 0) {
            exception('该支付无需支付!');
        }

        $user = User::getByUid($orderInfo['uid']);

        $openid = $user['wxapp_openid'];

        $type = 'payment';
        if($trade_type == 'APP'){
            $type = 'wxapp';
        }

        $app = self::payment($type);

        //生成支付订单
        $isContract = false;

        if($attach == 'recharge_balance'){
            $body = '瓜瓜配送充值';
        }else{
            $body = '瓜瓜配送下单';
        }

        $request = [
            'body' => $body,
            'detail' => $body,
            'out_trade_no' => $orderInfo->order_id,
            'total_fee' => $orderInfo['pay_price'] * 100,
            'spbill_create_ip' => '', // 可选，如不传该参数，SDK 将会自动获取相应 IP 地址
            'trade_type' => $trade_type, // 请对应换成你的支付方式对应的值类型
            'attach' => $attach,
        ];
        if($trade_type == 'JSAPI'){
            $request['openid'] = $openid;
        }
        if($trade_type == 'NATIVE'){
            $request['product_id'] = '';//商品ID 必填
        }

        $result = $app->order->unify($request, $isContract);

        if ($result['return_code'] == 'FAIL') {
            PayRequestData::record($orderInfo->order_id, 'payment.order.unify', 1, $request, $result, 0);
            exception("订单创建失败:{$result['return_msg']}");
        }
        if ($result['result_code'] == 'FAIL') {
            PayRequestData::record($orderInfo->order_id, 'payment.order.unify', 1, $request, $result, 0);
            exception("订单创建失败:{$result['result_code']}");
        }

        $platform = request()->header('platform','');
        $curTime = time();
        $secondSign = '';
        if($platform == 'Android'){
            $sign = "appid=" .$result['appid']. "&noncestr=" .$result['nonce_str']. "&package=Sign=WXPay" . "&partnerid=" .$result['mch_id'].
                "&prepayid=" .$result['prepay_id']. "&timestamp=" .$curTime. "&key=" .ConfigService::getSysValueByType(9)['apikey'];
            $secondSign = md5($sign);
            $result['timestamp'] = $curTime;
        }
        $result['secondSign'] = $secondSign;

        PayRequestData::record($orderInfo->order_id, 'payment.order.unify', 1, $request, $result, 1);
        return $result;
    }
}
